package br.com.educa.componentes.controller;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.TypedQuery;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import br.com.educa.componentes.datatables.DataTablesHql;
import br.com.educa.componentes.datatables.DataTablesSql;
import br.com.educa.componentes.datatables.DataTablesVO;
import br.com.educa.util.ConvertUtil;
import br.com.educa.util.MethodUtils;
import br.com.educa.util.ReflectionUtils;
import flexjson.JSONSerializer;

@SuppressWarnings("rawtypes")
public abstract class ComponentsController {
	
	@Autowired
	MethodUtils methodUtils;

	@Transactional(readOnly=true)
	@RequestMapping(value = "/combo/{propriedade}",headers = "Accept=application/json")
	@ResponseBody
	public ResponseEntity<String> listToComboBoxJson(@PathVariable("propriedade") String propriedade,
			@RequestParam(value = "class", required = true) String className,
			@RequestParam(value = "methodName", required = false) String methodName,
			HttpServletRequest httpServletRequest) {

		List result = null;
		ReflectionUtils reflectionUtils = new ReflectionUtils(className);
		
		if(methodName!=null && methodName!="" && methodName!= " "){
			Method finderMethod = reflectionUtils.getMethodByName(methodName);
			Class[] classesDosParametros = finderMethod.getParameterTypes();
			String[] finderParameters = methodUtils.getParameters(finderMethod);
			ArrayList<Object> args = new ArrayList<Object>();
			
			@SuppressWarnings("unchecked")
			Map<String,String[]> parameters = httpServletRequest.getParameterMap();
			for (Entry<String,String[]> entry : parameters.entrySet()) {
				for (int i = 0; i < finderParameters.length; i++) {
					if(entry.getKey().equals(finderParameters[i])){
						args.add(entry.getValue()[0]);
					}
				}
			}
			
			Object[] argsParams = args.toArray();
			for (int i = 0; i < argsParams.length; i++) {
				if(classesDosParametros[i].isPrimitive())
					argsParams[i] = ConvertUtil.convert(argsParams[i].toString(), classesDosParametros[i]);
				else if(ReflectionUtils.isWrapperType(classesDosParametros[i])){
					argsParams[i] = ConvertUtil.convert(argsParams[i].toString(), classesDosParametros[i]);
//					argsParams[i] = classesDosParametros[i].cast(argsParams[i]);
				}else if (classesDosParametros[i].isEnum()){
					for(Object constante : classesDosParametros[i].getEnumConstants()){
						if(constante.toString().equals(argsParams[i].toString())){
							argsParams[i] = constante;
						}
					}
				}else {
					ReflectionUtils tmp = new ReflectionUtils(classesDosParametros[i].getCanonicalName());
					argsParams[i] = classesDosParametros[i].cast(tmp.findById(Long.parseLong(argsParams[i].toString())));
				}
			}
			
			Object  resultado = reflectionUtils.find(methodName, argsParams);
			TypedQuery typedQuery = (TypedQuery) resultado;
			result = typedQuery.getResultList();
			
		} else {
			result = reflectionUtils.findAll();
		}

		
		HttpHeaders headers = new HttpHeaders();
		headers.add("Content-Type", "application/json; charset=utf-8");
		String combo = toJsonCombo(propriedade, result);
		return new ResponseEntity<String>(combo, headers, HttpStatus.OK);
	}

	@Transactional(readOnly=true)
	@RequestMapping(value = "/combo/{propriedade}/{propriedade1}",headers = "Accept=application/json")
	@ResponseBody
	public ResponseEntity<String> listToComboBoxTwoPropertiesJson(@PathVariable("propriedade") String propriedade,
			@PathVariable("propriedade1") String propriedade1,	
			@RequestParam(value = "class", required = true) String className) {

		HttpHeaders headers = new HttpHeaders();
		headers.add("Content-Type", "application/json; charset=utf-8");

		List result = null;
		try {
			Class clazz = Class.forName(className); 
			Object instance = clazz.newInstance();

			Method m[] = clazz.getDeclaredMethods(); 
			for (Method method : m) {
				if(method.getName().startsWith("findAll") && !method.getName().contains("_around")){
					result=(List) method.invoke(instance,new Object[0]);
					break;
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		String combo = toJsonComboTwoProperties(propriedade,propriedade1, result);
		return new ResponseEntity<String>(combo, headers, HttpStatus.OK);
	}


	private String toJsonComboTwoProperties(String propriedade,String propriedade1, List result){
		return new JSONSerializer().include("id").include(propriedade).include(propriedade1).exclude("*").serialize(result);
	}

	protected String toJsonCombo(String propriedade, List result){
		return new JSONSerializer().include("id").include(propriedade).exclude("*").serialize(result);
	}

	@Transactional(readOnly=true)
	@RequestMapping(value="/bServerSide",headers = "Accept=application/json")
	@ResponseBody
	public ResponseEntity<String> listServerProcessingJson(
			@RequestParam("where") String where, 
			@RequestParam("table") String table,
			@RequestParam(value = "class", required = true) String className,
			HttpServletRequest httpServletRequest) {

		@SuppressWarnings("unchecked")
		Map<String,String[]> parameters = httpServletRequest.getParameterMap();

		String sColumns = parameters.get("sColumns")[0];
		Integer sEcho = Integer.parseInt(parameters.get("sEcho")[0]);		

		try {
			ReflectionUtils reflectionUtils = new ReflectionUtils(className);
			DataTablesVO retorno = new DataTablesVO();
			retorno.setAaData(reflectionUtils.getResultList(DataTablesSql.doBServerSideSql(parameters, table, where)));
			retorno.setiTotalDisplayRecords(reflectionUtils.getSingleResultAsLong(DataTablesSql.doBServerSideITotalDisplayRecordsSql(parameters, table, where)));
			retorno.setiTotalRecords(reflectionUtils.getSingleResultAsLong(DataTablesSql.doBServerSideITotalRecordsSql(parameters, table, where)));
			retorno.setsColumns(sColumns);
			retorno.setsEcho(sEcho);

			HttpHeaders headers = new HttpHeaders();
			headers.add("Content-Type", "application/text; charset=utf-8");
			String json = new JSONSerializer().exclude("*.class").include("aaData").serialize(retorno).replace(":null", ":\"\"");

			return new ResponseEntity<String>(json, headers, HttpStatus.OK);

		}catch(Exception e){
			e.printStackTrace();
			HttpHeaders headers = new HttpHeaders();
			headers.add("Content-Type", "application/text; charset=utf-8");
			return new ResponseEntity<String>(headers, HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
	
	@Transactional(readOnly=true)
	@RequestMapping(value="/bServerSideHql",headers = "Accept=application/json")
	@ResponseBody
	public ResponseEntity<String> listServerProcessingHqlJson(
			@RequestParam("whereInicial") String whereInicial, 
			@RequestParam("whereAdicional") String whereAdicional,
			@RequestParam(value = "table", required = false) String table,
			@RequestParam(value = "class", required = true) String className,
			HttpServletRequest httpServletRequest) {

		@SuppressWarnings("unchecked")
		Map<String,String[]> parameters = httpServletRequest.getParameterMap();
		
		String sColumns = parameters.get("sColumns")[0];
		Integer sEcho = Integer.parseInt(parameters.get("sEcho")[0]);		

		try {
			DataTablesVO retorno = new DataTablesVO();
			
			List aaData = DataTablesHql.doBServerSideHql(parameters, className, table, whereInicial,whereAdicional).getResultList();
			Long iTotalRecords = (Long) (DataTablesHql.doBServerSideITotalRecordsSql(parameters, className, table,  whereInicial,whereAdicional).getSingleResult());
			
			retorno.setAaData(aaData);
			retorno.setiTotalDisplayRecords((long) aaData.size());
			retorno.setiTotalRecords(iTotalRecords);
			retorno.setsColumns(sColumns);
			retorno.setsEcho(sEcho);

			HttpHeaders headers = new HttpHeaders();
			headers.add("Content-Type", "application/text; charset=utf-8");

			JSONSerializer serializer = new JSONSerializer().exclude("*.class").include("aaData");
			for (String column : DataTablesHql.doJsonIncludeQuery(parameters)) {
				serializer.include(column);
			}
			String json = serializer.serialize(retorno).replace(":null", ":\"\"");

			return new ResponseEntity<String>(json, headers, HttpStatus.OK);

		}catch(Exception e){
			e.printStackTrace();
			HttpHeaders headers = new HttpHeaders();
			headers.add("Content-Type", "application/text; charset=utf-8");
			return new ResponseEntity<String>(headers, HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
	
	@Transactional(readOnly=true)
	@RequestMapping(value = "/{id}", params = "formJq", headers = "Accept=application/json", produces = "text/html")
    public String updateFormJson(@PathVariable("id") Long id, 
    		@RequestParam(value = "class", required = true) String className,
    		Model uiModel) {
		
		ReflectionUtils reflectionUtils = new ReflectionUtils(className);
    	String json = reflectionUtils.transformInJson(reflectionUtils.findById(id));
    	uiModel.addAttribute("json",json);
    	String pasta = className.split("\\.")[className.split("\\.").length-1].toLowerCase();
    	String pagina = "atualizar"+className.split("\\.")[className.split("\\.").length-1];
    	return pasta+"/"+pagina;
    }

	
}
